/**
  ******************************************************************************
  * @file    mesh_router_common.c
  * @author  zhuang
  * @version 1.0.0
  * @date    2021/1/20
  * @brief   
  ******************************************************************************
  * @attention
  *
  *
  ******************************************************************************
  */ 
  
/* Includes ------------------------------------------------------------------*/
#include "mdf_common.h"
#include "mlink.h"
#include "mupgrade.h"

#include "mesh_router_common.h"

#include "mesh_mqtt_handle.h"
#include "mlink_mqtt_handle.h"

#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
#include "app_aes.h"
#endif

/* Public variables ----------------------------------------------------------*/
static TaskHandle_t g_root_read_task_handle  = NULL;
static int *g_root_read_task_status = NULL;
#ifdef CONFIG_MQTT_ENABLE
static TaskHandle_t g_root_mqtt_read_task_handle  = NULL;
static int *g_root_mqtt_read_task_status = NULL;
#endif

EventGroupHandle_t g_event_group_task_iwdg = NULL;

/* Public functions ----------------------------------------------------------*/
extern const char * device_get_secret();
extern const char * device_get_secret_iv();

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/
#define TASK_RUNNING 1
#define TASK_STOP 0

#define TASK_BLOCK_TIME (1000)
/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static const char *TAG                       = "mesh_com";


/*
* mqtt 发送数据
*/
int mlink_mqtt_write(char **data, int size)
{
	if (!mesh_mqtt_is_connect()) {
		return MDF_FAIL;
	}

	mdf_err_t ret = MDF_OK;
	static uint8_t sta_mac[6]				= {0};
	if(sta_mac[0] == 0){
		MDF_ERROR_ASSERT(esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac));
	}
#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
	if(device_secret_is_enable()){
		char * out_data;
		size_t out_size;
		ret = app_aes_encrypt_bytes((const char *)(*data), size, &out_data, &out_size, device_get_secret(), device_get_secret_iv() );
		MDF_FREE(*data);
		*data = out_data;
		size = out_size;
		ret = mesh_mqtt_write(sta_mac, *data, size, MESH_MQTT_DATA_BYTES);
	}
	else
#endif
	{
		ret = mesh_mqtt_write(sta_mac, *data, size, MESH_MQTT_DATA_JSON);
	}

	return ret;
}

/**
 * @brief Handling data between wifi mesh devices.
 */
mdf_err_t node_handle_task(uint8_t request_protocol, mlink_httpd_type_t httpd_type, const char *data, size_t size)
{
    mdf_err_t ret = MDF_OK;

    uint8_t src_addr[MWIFI_ADDR_LEN] = {0x0};
    mlink_httpd_type_t *header_info  = NULL;
	char request_id[8] = {0x0};


	MDF_ERROR_ASSERT(esp_wifi_get_mac(ESP_IF_WIFI_STA, src_addr));
    MDF_LOGD("Node receive, size: %d, data: %.*s", size, size, data);

    /*< Header information for http data */
    header_info = &httpd_type;
    MDF_ERROR_CHECK(header_info->format != MLINK_HTTPD_FORMAT_JSON, MDF_FAIL,
                   "The current version only supports the json protocol");

    /**
     * @brief Processing request commands, generating response data
     *
     * @note  Handling only the body part of http, the header
     *        of http is handled by mlink_httpd
     */
    mlink_handle_data_t handle_data = {
        .req_data    = (char *)data,
        .req_size    = size,
        .req_fromat  = MLINK_HTTPD_FORMAT_JSON,
        .resp_data   = NULL,
        .resp_size   = 0,
        .resp_fromat = MLINK_HTTPD_FORMAT_JSON,
    };
    ret = mlink_handle_request(&handle_data);

    if (handle_data.resp_fromat == MLINK_HTTPD_FORMAT_JSON) {
#ifdef CONFIG_MQTT_ENABLE
		if(request_protocol == MDF_MLINK_PROTO_CUSTOM_MQTT) {
			// set "request_id":"xxxxxx" "type":"response"
			mlink_json_pack(&handle_data.resp_data, "type", "response");
			if(mlink_json_parse((char *)data, "request_id", request_id) == MDF_OK) {
				mlink_json_pack(&handle_data.resp_data, "request_id", request_id);
			}
		}
#endif
        mlink_json_pack(&handle_data.resp_data, "status_msg", mdf_err_to_name(ret));
        handle_data.resp_size = mlink_json_pack(&handle_data.resp_data, "status_code", -ret);
    }

    /**
     * @brief Send the response data to the source device
     */
    if (header_info->resp) {
		
		if(request_protocol == MLINK_PROTO_HTTPD){
			mlink_httpd_t httpd_resp_data  = {
				.size		= handle_data.resp_size,
				.data		= handle_data.resp_data,
				.addrs_num	= 1,
				.addrs_list = src_addr,
			};
			memcpy(&httpd_resp_data.type, &httpd_type, sizeof(httpd_resp_data.type));
#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
			if(device_secret_is_enable()){
				char * out_data;
				size_t out_size;
				ret = app_aes_encrypt((const char *)handle_data.resp_data, handle_data.resp_size, &out_data, &out_size, device_get_secret(), device_get_secret_iv() );
				MDF_ERROR_GOTO(ret != MDF_OK, FREE_MEM, "AES Encryptvfail");
				MDF_FREE(handle_data.resp_data);
				handle_data.resp_data = out_data;
				handle_data.resp_size = out_size;
				httpd_resp_data.data = out_data;
				httpd_resp_data.size = out_size;
			}
#endif
			ret = mlink_httpd_write(&httpd_resp_data, portMAX_DELAY);
		}
		else if(request_protocol == MDF_MLINK_PROTO_CUSTOM_MQTT) {
#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
			if(device_secret_is_enable()){
				char * out_data;
				size_t out_size;
				ret = app_aes_encrypt_bytes((const char *)handle_data.resp_data, handle_data.resp_size, &out_data, &out_size, device_get_secret(), device_get_secret_iv() );
				MDF_ERROR_GOTO(ret != MDF_OK, FREE_MEM, "AES Encryptvfail");
				MDF_FREE(handle_data.resp_data);
				handle_data.resp_data = out_data;
				handle_data.resp_size = out_size;
				ret = mesh_mqtt_write(src_addr, handle_data.resp_data, handle_data.resp_size, MESH_MQTT_DATA_BYTES);
			}else
#endif
			{
				ret = mesh_mqtt_write(src_addr, handle_data.resp_data, handle_data.resp_size, MESH_MQTT_DATA_JSON);
				MDF_ERROR_GOTO(ret != MDF_OK, FREE_MEM, "mesh_mqtt_write");
			}
		}
        if (handle_data.resp_fromat == MLINK_HTTPD_FORMAT_HEX) {
            MDF_LOGD("Node send, size: %d, data: ", handle_data.resp_size);
            ESP_LOG_BUFFER_HEX(TAG, handle_data.resp_data, handle_data.resp_size);
        } else {
            MDF_LOGD("Node send, size: %d, data: %.*s", handle_data.resp_size,
                     handle_data.resp_size, handle_data.resp_data);
        }
    }
FREE_MEM:
    MDF_FREE(handle_data.resp_data);

	return ret;
}

#ifdef CONFIG_MLINK_HTTPD_ENABLE
/**
 * @brief Read data from extern IP network, forward data to destination device.
 */
void root_read_task(void *arg)
{
	int *g_root_read_task_status = (int *)arg;
    mdf_err_t ret               = MDF_OK;
    mlink_httpd_t *httpd_data   = NULL;
	uint8_t self_mac[6] 		 = {0};
	esp_wifi_get_mac(ESP_IF_WIFI_STA, self_mac);

    MDF_LOGI("root_read_task is running");

    while (*g_root_read_task_status == TASK_RUNNING) {
        ret = mlink_httpd_read(&httpd_data, portMAX_DELAY);
        MDF_ERROR_GOTO(ret != MDF_OK || !httpd_data, FREE_MEM, "<%s> mwifi_root_read", mdf_err_to_name(ret));
        MDF_LOGD("Root send size: %d, data: %.*s",
			httpd_data->size, httpd_data->size, httpd_data->data);

#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
		if(device_secret_is_enable()){
			char * out_data;
			size_t out_size;
			ret= app_aes_decrypt((const char *)httpd_data->data, httpd_data->size, 
								 &out_data, &out_size, device_get_secret(), device_get_secret_iv());
			MDF_ERROR_GOTO(ret != MDF_OK, FREE_MEM, "AES Decrypt fail");
			MDF_FREE(httpd_data->data);
			httpd_data->data = out_data;
			httpd_data->size = out_size;
		}
#endif
#if 0//MLINK_MQTT_DEBUG
		char *resp_data 				  = NULL;
		int resp_size;
		mlink_json_pack(&resp_data, "data", httpd_data->data);
		resp_size = mlink_json_pack(&resp_data, "type", "log");
		ret = mlink_mqtt_write(&resp_data, resp_size);
		MDF_FREE(resp_data);
#endif

		// if(httpd_data->espnow){
		// 	mlink_espnow_write(httpd_data->addrs_list, httpd_data->addrs_num, 
		// 		               httpd_data->data, httpd_data->size, httpd_data->communicate_type, portMAX_DELAY);

		// }

		for (int i = 0; i < httpd_data->addrs_num; ++i) {
			bool handle_data = false;
			mesh_addr_t dest_addr;
			memcpy(dest_addr.addr, httpd_data->addrs_list + i*6, MWIFI_ADDR_LEN);

			if (MWIFI_ADDR_IS_EMPTY(dest_addr.addr)
				|| MWIFI_ADDR_IS_ANY(dest_addr.addr)
				//|| MWIFI_ADDR_IS_BROADCAST(dest_addr.addr)
				|| !memcmp(dest_addr.addr, self_mac, MWIFI_ADDR_LEN)) {
				handle_data = true;
			} 
			/*else if( httpd_data->communicate_type == MLINK_ESPNOW_COMMUNICATE_GROUP && esp_mesh_is_my_group(&dest_addr)) {
				handle_data = true;
			}*/
			if(handle_data){
				ret = node_handle_task(MLINK_PROTO_HTTPD, httpd_data->type, httpd_data->data, httpd_data->size);
				MDF_ERROR_GOTO(ret != MDF_OK, FREE_MEM, "<%s> mwifi_root_write", mdf_err_to_name(ret));
				break;
			}
		}
FREE_MEM:

        if (httpd_data) {
            MDF_FREE(httpd_data->addrs_list);
            MDF_FREE(httpd_data->data);
            MDF_FREE(httpd_data);
        }
    }

    MDF_LOGW("root_read_task is exit");

    if (httpd_data) {
        MDF_FREE(httpd_data->addrs_list);
        MDF_FREE(httpd_data->data);
        MDF_FREE(httpd_data);
    }
	
	MDF_FREE(g_root_read_task_status);
    g_root_read_task_handle = NULL;
    vTaskDelete(NULL);
}
#endif

void mlink_handle_request_noresp_task(void *arg)
{
    mdf_err_t ret               = MDF_OK;
    mlink_handle_data_t *mlink_handle_data   = NULL;

    MDF_LOGI("mlink_handle_request_noresp_task is running");

    while (1) {
        ret = mlink_handle_request_read(&mlink_handle_data, portMAX_DELAY);
        MDF_LOGI("mlink_handle size: %d, data: %.*s",
			mlink_handle_data->req_size, mlink_handle_data->req_size, mlink_handle_data->req_data);
		ret = mlink_handle_request(mlink_handle_data);

        if (mlink_handle_data) {
            MDF_FREE(mlink_handle_data->resp_data);
            MDF_FREE(mlink_handle_data->req_data);
            MDF_FREE(mlink_handle_data);
        }
    }

    MDF_LOGW("mlink_handle_request_noresp_task is exit");

    vTaskDelete(NULL);
}


#ifdef CONFIG_MQTT_ENABLE

void root_mqtt_read_task(void *arg)
{
	int *g_root_mqtt_read_task_status = (int *)arg;
    mdf_err_t ret = MDF_OK;

	mlink_httpd_type_t httpd_type = {
		.format = MLINK_HTTPD_FORMAT_JSON,
		.from = MLINK_HTTPD_FROM_SERVER,
		.resp = true,
	};
	uint8_t self_mac[6] 		 = {0};
	
	esp_wifi_get_mac(ESP_IF_WIFI_STA, self_mac);

    MDF_LOGI("Root mqtt read task is running");

    while (*g_root_mqtt_read_task_status == TASK_RUNNING) {

        if (!mesh_mqtt_is_connect()) {
            vTaskDelay(pdMS_TO_TICKS(500));
            continue;
        }

        mesh_mqtt_data_t *request = NULL;
        /**
         * @brief Recv data from mqtt data queue, and forward to special device.
         */
		ret = mesh_mqtt_read(&request, pdMS_TO_TICKS(TASK_BLOCK_TIME));
        if (ret != MDF_OK) {
            continue;
        }
#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
		if(device_secret_is_enable()){
			char * out_data;
			size_t out_size;
			ret= app_aes_decrypt_bytes((const char *)request->data, request->size, 
								 &out_data, &out_size, device_get_secret(), device_get_secret_iv());
			MDF_ERROR_GOTO(ret != MDF_OK, MEM_FREE, "AES Decrypt fail");
			MDF_FREE(request->data);
			request->data = out_data;
			request->size = out_size;
		}
#endif
		// if(request->espnow){
		// 	mlink_espnow_write(request->addrs_list, request->addrs_num, 
		// 		               request->data, request->size, request->communicate_type, portMAX_DELAY);
		// }
		{
			for (int i = 0; i < request->addrs_num; ++i) {
				bool handle_data = false;
				mesh_addr_t dest_addr;
				memcpy(dest_addr.addr, request->addrs_list + i*6, MWIFI_ADDR_LEN);

				if (MWIFI_ADDR_IS_EMPTY(dest_addr.addr)
					|| MWIFI_ADDR_IS_ANY(dest_addr.addr)
					//|| MWIFI_ADDR_IS_BROADCAST(dest_addr.addr)
					|| !memcmp(dest_addr.addr, self_mac, MWIFI_ADDR_LEN)) {
					handle_data = true;
				} 

				if(handle_data){
					ret = node_handle_task(MDF_MLINK_PROTO_CUSTOM_MQTT, httpd_type, request->data, request->size);
					MDF_ERROR_GOTO(ret != MDF_OK, MEM_FREE, "node_handle_task");
				}
			}
		}
MEM_FREE:
        MDF_FREE(request->addrs_list);
        MDF_FREE(request->data);
        MDF_FREE(request);
    }

    MDF_LOGW("Root mqtt read task is exit");
	MDF_FREE(g_root_mqtt_read_task_status);
	g_root_mqtt_read_task_handle = NULL;
    vTaskDelete(NULL);
}

mdf_err_t mqtt_heartbeat_send(void)
{
    mdf_err_t ret = MDF_OK;
    size_t size = 0;
    char *data = NULL;
    uint8_t sta_mac[MWIFI_ADDR_LEN] = { 0 };

	if (!mesh_mqtt_is_connect()) {
		return MDF_FAIL;
	}

	/**
	 * @brief Send device information to mqtt server throught root node.
	 */
	esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac);
	time_t now=0;
	time(&now);
	size = asprintf(&data, "{\"version\":\"%s\",\"type\":\"heartbeat\",\"self\":\"%02x%02x%02x%02x%02x%02x\",\"time\":%lld,\"running_time\":\"%lld\"}",
					CONFIG_APP_VERSION, MAC2STR(sta_mac), now, esp_timer_get_time());
	
	MDF_LOGD("Node send, size: %d, data: %s", size, data);
#ifdef CONFIG_MLINK_HTTP_USE_AES_ENCRYPTION
	if(device_secret_is_enable()){
		char * out_data;
		size_t out_size;
		ret = app_aes_encrypt_bytes((const char *)data, size, &out_data, &out_size, device_get_secret(), device_get_secret_iv() );
		if(ret != MDF_OK) MDF_LOGE("AES Encryptvfail");
		MDF_FREE(data);
		data = out_data;
		size = out_size;
		ret = mesh_mqtt_write(sta_mac, data, size, MESH_MQTT_DATA_BYTES);
		if(ret != MDF_OK) MDF_LOGE("<%s> mesh_mqtt_publish", mdf_err_to_name(ret));
	}else
#endif
	{
		ret = mesh_mqtt_write(sta_mac, data, size, MESH_MQTT_DATA_JSON);
		if(ret != MDF_OK) MDF_LOGE("<%s> mesh_mqtt_publish", mdf_err_to_name(ret));
	}

	MDF_FREE(data);
	return ret;
}

#endif

#if 0//CONFIG_ESPNOW_ENABLE
/**
 * @brief Initialize espnow_to_mwifi_task for forward esp-now data to the wifi mesh network.
 */
void espnow_to_mwifi_task(void *arg)
{
    mdf_err_t ret       = MDF_OK;
    uint8_t *data       = NULL;
    uint8_t *addrs_list = NULL;
    size_t addrs_num    = 0;
    size_t size         = 0;
    uint32_t type       = 0;
	uint8_t self_mac[6]          = {0};
	
	esp_wifi_get_mac(ESP_IF_WIFI_STA, self_mac);

    while (mlink_espnow_read(&addrs_list, &addrs_num, &data, &size, &type, portMAX_DELAY) == MDF_OK) {

        bool group = (type == MLINK_ESPNOW_COMMUNICATE_GROUP) ? true : false;
        MDF_LOGI("Mlink espnow read data: %.*s", size, data);

        for (int i = 0; i < addrs_num; ++i) {
			
			bool handle_data = false;
			mesh_addr_t dest_addr;
			memcpy(dest_addr.addr, addrs_list + i*6, MWIFI_ADDR_LEN);

			if (MWIFI_ADDR_IS_EMPTY(dest_addr.addr)
				|| MWIFI_ADDR_IS_ANY(dest_addr.addr)
				|| MWIFI_ADDR_IS_BROADCAST(dest_addr.addr)
				|| !memcmp(dest_addr.addr, self_mac, MWIFI_ADDR_LEN)) {
				handle_data = true;
			} 

			if(handle_data){
				mlink_handle_data_t handle_data = {
					.req_data	 = (char *)data,
					.req_size	 = size,
					.req_fromat  = MLINK_HTTPD_FORMAT_JSON,
					.resp_data	 = NULL,
					.resp_size	 = 0,
					.resp_fromat = MLINK_HTTPD_FORMAT_JSON,
				};
				ret = mlink_handle_request(&handle_data);
				MDF_FREE(handle_data.resp_data);
			}

        }

        MDF_FREE(data);
        MDF_FREE(addrs_list);
    }

    MDF_LOGW("espnow_to_mwifi_task is exit");
    vTaskDelete(NULL);
}
#endif

/**
 * @brief scan trigger.
 */
void trigger_timming_handle(time_t nowtime)
{
	static int pre_min = -1;
    struct tm timeinfo;
	localtime_r(&nowtime, &timeinfo);
	char strftime_buf[64];
	strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
	MDF_LOGD("The current date/time in Shanghai is: %s", strftime_buf);

	if(pre_min != timeinfo.tm_min && !(timeinfo.tm_year < (2020 - 1900)) ){
		pre_min = timeinfo.tm_min;
		mlink_trigger_timming_handle(MLINK_COMMUNICATE_MESH);
	}
}


mdf_err_t mesh_root_task_init()
{
	mdf_err_t ret;
	/**
	 * @brief Initialization mlink notice for inform the mobile phone that there is a mesh root device
	 */
	ret = mlink_notice_init();
	if(ret != MDF_OK) MDF_LOGE("<%s> mlink_notice_init", mdf_err_to_name(ret));

	uint8_t sta_mac[MWIFI_ADDR_LEN] = {0x0};
	MDF_ERROR_ASSERT(esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac));

	ret = mlink_notice_write("http", strlen("http"), sta_mac);
	if(ret != MDF_OK) MDF_LOGE("<%s> mlink_notice_write", mdf_err_to_name(ret));
#ifdef CONFIG_MLINK_HTTPD_ENABLE
	// mlink_httpd_start();

	if (g_root_read_task_status == NULL) {
		g_root_read_task_status = MDF_MALLOC(sizeof(int));
        *g_root_read_task_status = TASK_RUNNING;
		xTaskCreate(root_read_task, "root_read", 4 * 1024,
					g_root_read_task_status, CONFIG_MDF_TASK_DEFAULT_PRIOTY, &g_root_read_task_handle);
	}
#endif
#ifdef CONFIG_MQTT_ENABLE
	mesh_mqtt_start(mqtt_get_url(), mqtt_get_port(),  mqtt_get_username(), mqtt_get_password() );
	if (g_root_mqtt_read_task_status == NULL) {
		g_root_mqtt_read_task_status = MDF_MALLOC(sizeof(int));
        *g_root_mqtt_read_task_status = TASK_RUNNING;
		xTaskCreate(root_mqtt_read_task, "root_mqtt_read", 4 * 1024,
					g_root_mqtt_read_task_status, CONFIG_MDF_TASK_DEFAULT_PRIOTY, &g_root_mqtt_read_task_handle);
	}
#endif
	return ret;
}

mdf_err_t mesh_root_task_deinit()
{
	mdf_err_t ret;

    if (g_root_read_task_status != NULL) {
        *g_root_read_task_status = TASK_STOP;
        g_root_read_task_status = NULL;
    }
#ifdef CONFIG_MQTT_ENABLE
    if (g_root_mqtt_read_task_status != NULL) {
        *g_root_mqtt_read_task_status = TASK_STOP;
        g_root_mqtt_read_task_status = NULL;
    }
#endif	
	ret = mlink_notice_deinit();
	if(ret != MDF_OK) MDF_LOGE("<%s> mlink_notice_deinit", mdf_err_to_name(ret));

	ret = mlink_httpd_stop();
	if(ret != MDF_OK) MDF_LOGE("<%s> mlink_httpd_stop", mdf_err_to_name(ret));
	
#ifdef CONFIG_MQTT_ENABLE
	ret = mesh_mqtt_stop();
	if(ret != MDF_OK) MDF_LOGE("<%s> mesh_mqtt_stop", mdf_err_to_name(ret));
#endif

    return ret;

}

